using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Application = Autodesk.AutoCAD.ApplicationServices.Core.Application;

namespace AutoCAD_CSharp_plug_in3.Chap01
{
    /// <summary>
    ///     打断类型枚举
    /// </summary>
    public enum BreakType
    {
        Line, // 线段交点打断
        Circle, // 圆心打断  
        Insert, // 块插入点打断
        Polyline // 多段线打断
    }

    /// <summary>
    ///     线段打断处理
    /// </summary>
    public static class LineBreakAction
    {
        #region 主处理

        /// <summary>
        ///     处理打断操作
        /// </summary>
        /// <param name="entityTypes">选择的类型</param>
        /// <param name="enableLineIntert">是否启用线段交点打断</param>
        public static void ProcessSelectedObj(List<string> entityTypes, bool enableLineIntert)
        {
            var context = new ProcessingContext();

            if (!SelectEntities(entityTypes, context))
                return;

            using (var transaction = context.Database.TransactionManager.StartTransaction())
            using (context.Document.LockDocument())
            {
                if (!ClassifySelectedEntities(transaction, context))
                    return;

                var breakPointMap = CalculateBreakPoints(context, enableLineIntert);
                ApplyLineBreaks(context.Database, transaction, breakPointMap);

                transaction.Commit();
            }
        }

        #endregion

        #region 验证Z坐标

        /// <summary>
        ///     验证Z坐标是否有效（为0）
        /// </summary>
        private static bool IsValidZCoordinate(double z)
        {
            return Math.Abs(z) < PrecisionTolerance;
        }

        #endregion

        #region 常量

        private const double ProximityTolerance = 1.0; // 块圆打断距离阈值
        private const double PrecisionTolerance = 0.0001; // 端点精度阈值

        #endregion

        #region 选择指定分类

        /// <summary>
        ///     选择指定类型的实体
        /// </summary>
        private static bool SelectEntities(List<string> entityTypes, ProcessingContext context)
        {
            // fixme filter 暂时不使用，服务过滤
            var filter = CreateEntityFilter(entityTypes);
            // 创建选择选项
            PromptSelectionOptions pso = new PromptSelectionOptions
            {
                MessageForAdding = $"\n请选择{string.Join("、", entityTypes)}: ",
                AllowDuplicates = false,
                RejectObjectsFromNonCurrentSpace = true,
                RejectObjectsOnLockedLayers = true
            };
            var selectionResult = context.Editor.GetSelection(pso);

            if (selectionResult.Status != PromptStatus.OK)
                return false;

            context.SelectionSet = selectionResult.Value;
            return true;
        }

        /// <summary>
        ///     创建实体过滤器
        /// </summary>
        private static SelectionFilter CreateEntityFilter(List<string> entityTypes)
        {
            var filterList = new TypedValue[entityTypes.Count + 2];
            filterList[0] = new TypedValue((int)DxfCode.Operator, "<or");
            filterList[entityTypes.Count + 1] = new TypedValue((int)DxfCode.Operator, "or>");

            for (var i = 0; i < entityTypes.Count; i++)
                filterList[i + 1] = new TypedValue((int)DxfCode.Start, entityTypes[i]);

            return new SelectionFilter(filterList);
        }

        /// <summary>
        ///     分类选定的实体
        /// </summary>
        private static bool ClassifySelectedEntities(Transaction transaction, ProcessingContext context)
        {
            foreach (var objectId in context.SelectionSet.GetObjectIds())
            {
                var entity = (Entity)transaction.GetObject(objectId, OpenMode.ForWrite);

                if (!ProcessEntity(entity, context))
                    return false;
            }

            return true;
        }

        /// <summary>
        ///     处理单个实体
        /// </summary>
        private static bool ProcessEntity(Entity entity, ProcessingContext context)
        {
            switch (entity)
            {
                case Line line:
                    return ProcessLine(line, context);
                case Circle circle:
                    return ProcessCircle(circle, context);
                case BlockReference blockRef:
                    return ProcessBlockReference(blockRef, context);
                case Polyline polyline:
                    return ProcessPolyLine(polyline, context);
                default:
                    return true;
            }
        }

        private static bool ProcessPolyLine(Polyline polyline, ProcessingContext context)
        {
            context.Polylines.Add(polyline);
            return true;
        }

        private static bool ProcessLine(Line line, ProcessingContext context)
        {
            if (!IsValidZCoordinate(line.StartPoint.Z) || !IsValidZCoordinate(line.EndPoint.Z))
            {
                context.Editor.WriteMessage("发现线段Z轴未归零！");
                return false;
            }

            context.Lines.Add(line);
            return true;
        }

        private static bool ProcessCircle(Circle circle, ProcessingContext context)
        {
            if (!IsValidZCoordinate(circle.Center.Z))
            {
                context.Editor.WriteMessage("发现圆心Z轴未归零！");
                return false;
            }

            context.CirclePoints.Add(circle.Center);
            return true;
        }

        private static bool ProcessBlockReference(BlockReference blockRef, ProcessingContext context)
        {
            if (!IsValidZCoordinate(blockRef.Position.Z))
            {
                context.Editor.WriteMessage("发现块插入点Z轴未归零！");
                return false;
            }

            context.BlockPoints.Add(blockRef.Position);
            return true;
        }

        #endregion

        #region 打断点计算

        /// <summary>
        ///     计算所有线段的打断点
        /// </summary>
        private static Dictionary<Line, HashSet<Point3d>> CalculateBreakPoints(ProcessingContext context,
            bool enableLineIntersection)
        {
            var breakPointMap = new Dictionary<Line, HashSet<Point3d>>();

            // 处理块插入点打断
            AddProximityBreakPoints(context.BlockPoints, context.Lines, breakPointMap);

            // 处理圆心打断
            AddProximityBreakPoints(context.CirclePoints, context.Lines, breakPointMap);

            // 处理交点打断
            if (enableLineIntersection)
            {
                // 线段
                AddIntersectionBreakPoints(context.Lines, breakPointMap);
                // 多段线
                AddPolylineBreakPoints(context.Polylines, context.Lines,
                    context.BlockPoints, context.CirclePoints, breakPointMap);
            }

            return breakPointMap;
        }

        /// <summary>
        /// 处理多段线与其他曲线/线段的交点打断
        /// </summary>
        /// <param name="polylines">Polylines</param>
        /// <param name="lines">需要处理的直线集合</param>
        /// <param name="blocks">需要处理的直线集合</param>
        /// <param name="circles">需要处理的直线集合</param>
        /// <param name="breakPointMap">存储打断点的字典</param>
        private static void AddPolylineBreakPoints(List<Polyline> polylines, List<Line> lines,
            List<Point3d> blocks, List<Point3d> circles,
            Dictionary<Line, HashSet<Point3d>> breakPointMap)
        {
            var curves = new List<Curve>();
            foreach (var pl in polylines)
            {
                // 创建集合来存储炸开后的对象
                using (DBObjectCollection exploded = new DBObjectCollection())
                {
                    // 炸开多段线
                    pl.Explode(exploded);
                    // 处理每个炸开后的对象
                    foreach (DBObject obj in exploded)
                    {
                        if (obj is Curve curve)
                        {
                            curves.Add(curve);
                        }
                    }
                }

                pl.Erase();
            }

            // 处理多段线曲线与直线的交点
            foreach (Curve curve in curves)
            {
                // 1. 多段线曲线与直线的交点
                foreach (Line line in lines)
                {
                    if (line == null) continue;

                    // 获取交点
                    Point3dCollection intersectionPoints = new Point3dCollection();
                    curve.IntersectWith(line, Intersect.OnBothOperands, intersectionPoints, IntPtr.Zero,
                        IntPtr.Zero);

                    // 处理找到的交点
                    foreach (Point3d point in intersectionPoints)
                    {
                        // 确保Z坐标为0（如果是2D图形）
                        Point3d breakPoint = new Point3d(point.X, point.Y, 0);

                        // 为直线添加打断点
                        if (!breakPointMap.TryGetValue(line, out var breakPoints))
                        {
                            breakPoints = new HashSet<Point3d>();
                            breakPointMap[line] = breakPoints;
                        }

                        breakPoints.Add(breakPoint);

                        // 为多段线曲线添加打断点
                        // 由于多段线已经被炸开为单独的曲线，直接处理这些曲线
                        if (curve is Line plineSegment)
                        {
                            if (!breakPointMap.TryGetValue(plineSegment, out var plineBreakPoints))
                            {
                                plineBreakPoints = new HashSet<Point3d>();
                                breakPointMap[plineSegment] = plineBreakPoints;
                            }

                            plineBreakPoints.Add(breakPoint);
                        }
                    }
                }

                // 2. 多段线曲线之间的交点
                foreach (Curve otherCurve in curves)
                {
                    if (otherCurve == curve || otherCurve == null) continue;

                    Point3dCollection curveIntersections = new Point3dCollection();
                    curve.IntersectWith(otherCurve, Intersect.OnBothOperands, curveIntersections, IntPtr.Zero,
                        IntPtr.Zero);

                    foreach (Point3d point in curveIntersections)
                    {
                        Point3d breakPoint = new Point3d(point.X, point.Y, 0);

                        // 为当前曲线添加打断点
                        if (curve is Line line1)
                        {
                            if (!breakPointMap.TryGetValue(line1, out var breakPoints1))
                            {
                                breakPoints1 = new HashSet<Point3d>();
                                breakPointMap[line1] = breakPoints1;
                            }

                            breakPoints1.Add(breakPoint);
                        }

                        // 为另一条曲线添加打断点
                        if (otherCurve is Line line2)
                        {
                            if (!breakPointMap.TryGetValue(line2, out var breakPoints2))
                            {
                                breakPoints2 = new HashSet<Point3d>();
                                breakPointMap[line2] = breakPoints2;
                            }

                            breakPoints2.Add(breakPoint);
                        }
                    }
                }

                // 3.  多段线曲线与块插入点之间的交点
                foreach (var blkPt in blocks)
                {
                    if (curve is Line seg)
                    {
                        var foot = seg.GetClosestPointTo(blkPt, false);
                        var dist = foot.DistanceTo(blkPt);

                        if (dist < ProximityTolerance &&
                            !foot.IsSameAs(seg.StartPoint) &&
                            !foot.IsSameAs(seg.EndPoint))
                        {
                            if (!breakPointMap.TryGetValue(seg, out var segBreakPoints))
                            {
                                segBreakPoints = new HashSet<Point3d>();
                                breakPointMap[seg] = segBreakPoints;
                            }

                            // 统一Z=0
                            var bp = new Point3d(foot.X, foot.Y, 0);
                            segBreakPoints.Add(bp);
                        }
                    }
                }

                // 4.  多段线曲线与圆心之间的交点
                foreach (var cenPt in circles)
                {
                    if (curve is Line seg)
                    {
                        var foot = seg.GetClosestPointTo(cenPt, false);
                        var dist = foot.DistanceTo(cenPt);

                        if (dist < ProximityTolerance &&
                            !foot.IsSameAs(seg.StartPoint) &&
                            !foot.IsSameAs(seg.EndPoint))
                        {
                            if (!breakPointMap.TryGetValue(seg, out var segBreakPoints))
                            {
                                segBreakPoints = new HashSet<Point3d>();
                                breakPointMap[seg] = segBreakPoints;
                            }

                            // 统一Z=0
                            var bp = new Point3d(foot.X, foot.Y, 0);
                            segBreakPoints.Add(bp);
                        }
                    }
                }
            }
        }

        /// <summary>
        ///     添加基于距离的打断点（块和圆）
        /// </summary>
        private static void AddProximityBreakPoints(List<Point3d> points, List<Line> lines,
            Dictionary<Line, HashSet<Point3d>> breakPointMap)
        {
            foreach (var point in points)
            foreach (var line in lines)
            {
                if (ShouldBreakAtPoint(line, point))
                    AddBreakPoint(line, point, breakPointMap);
                line.Erase();
            }
        }

        /// <summary>
        ///     判断是否应在指定点打断线段
        /// </summary>
        private static bool ShouldBreakAtPoint(Line line, Point3d point)
        {
            var closestPoint = line.GetClosestPointTo(point, false);
            var distance = closestPoint.DistanceTo(point);

            return distance < ProximityTolerance &&
                   !point.IsSameAs(line.StartPoint) &&
                   !point.IsSameAs(line.EndPoint);
        }

        /// <summary>
        ///     添加线段交点打断
        /// </summary>
        private static void AddIntersectionBreakPoints(List<Line> lines,
            Dictionary<Line, HashSet<Point3d>> breakPointMap)
        {
            for (var i = 0; i < lines.Count; i++)
            for (var j = i + 1; j < lines.Count; j++)
                ProcessLineIntersection(lines[i], lines[j], breakPointMap);
            for (var i = 0; i < lines.Count; i++)
            {
                lines[i].Erase();
            }
        }

        /// <summary>
        ///     处理两条线段的交点
        /// </summary>
        private static void ProcessLineIntersection(Line line1, Line line2,
            Dictionary<Line, HashSet<Point3d>> breakPointMap)
        {
            var intersections = GetLineIntersections(line1, line2);

            foreach (var intersection in intersections)
            {
                ProcessIntersectionPoint(line1, intersection, breakPointMap);
                ProcessIntersectionPoint(line2, intersection, breakPointMap);
            }
        }

        /// <summary>
        ///     获取两条线段的交点
        /// </summary>
        private static List<Point3d> GetLineIntersections(Line line1, Line line2)
        {
            var intersections = new Point3dCollection();
            line1.IntersectWith(line2, Intersect.OnBothOperands, intersections, IntPtr.Zero, IntPtr.Zero);
            return intersections.Cast<Point3d>().ToList();
        }

        /// <summary>
        ///     处理单个交点
        /// </summary>
        private static void ProcessIntersectionPoint(Line line, Point3d intersection,
            Dictionary<Line, HashSet<Point3d>> breakPointMap)
        {
            var adjustedPoint = AdjustIntersectionPoint(line, intersection);

            if (IsValidBreakPoint(line, adjustedPoint))
            {
                var normalizedPoint = new Point3d(adjustedPoint.X, adjustedPoint.Y, 0);
                AddBreakPoint(line, normalizedPoint, breakPointMap);
            }
        }

        /// <summary>
        ///     调整交点位置以处理端点附近的情况
        /// </summary>
        private static Point3d AdjustIntersectionPoint(Line line, Point3d intersection)
        {
            if (intersection.DistanceTo(line.StartPoint) < PrecisionTolerance)
                return line.StartPoint;

            if (intersection.DistanceTo(line.EndPoint) < PrecisionTolerance)
                return line.EndPoint;

            return intersection;
        }

        /// <summary>
        ///     验证是否为有效的打断点
        /// </summary>
        private static bool IsValidBreakPoint(Line line, Point3d point)
        {
            if (point.IsSameAs(line.StartPoint) || point.IsSameAs(line.EndPoint))
                return false;

            var closestPoint = line.GetClosestPointTo(point, false);
            return closestPoint.DistanceTo(point) <= PrecisionTolerance;
        }

        /// <summary>
        ///     添加打断点到映射表
        /// </summary>
        private static void AddBreakPoint(Line line, Point3d point, Dictionary<Line, HashSet<Point3d>> breakPointMap)
        {
            if (!breakPointMap.ContainsKey(line)) breakPointMap[line] = new HashSet<Point3d>();

            breakPointMap[line].Add(point);
        }

        #endregion

        #region 线段分割实现

        /// <summary>
        ///     应用线段打断
        /// </summary>
        private static void ApplyLineBreaks(Database database, Transaction transaction,
            Dictionary<Line, HashSet<Point3d>> breakPointMap)
        {
            var blockTableRecord =
                (BlockTableRecord)transaction.GetObject(database.CurrentSpaceId, OpenMode.ForWrite);

            foreach (var kvp in breakPointMap)
            {
                var line = kvp.Key;
                var breakPoints = kvp.Value;

                if (breakPoints.Count > 0)
                {
                    var newLines = CreateSplitLines(line, breakPoints);
                    AddLinesToDatabase(blockTableRecord, transaction, newLines);
                    // line.Erase();
                }
            }
        }

        /// <summary>
        ///     创建分割后的线段
        /// </summary>
        private static List<Line> CreateSplitLines(Line originalLine, HashSet<Point3d> breakPoints)
        {
            var sortedPoints = breakPoints.ToList();
            sortedPoints.Sort((a, b) =>
            {
                var aDist = new Line(originalLine.StartPoint, a).Length;
                var bDist = new Line(originalLine.StartPoint, b).Length;
                return aDist.CompareTo(bDist);
            });
            return GenerateLineSegments(originalLine, sortedPoints);
        }

        /// <summary>
        ///     生成线段片段
        /// </summary>
        private static List<Line> GenerateLineSegments(Line originalLine, List<Point3d> breakPoints)
        {
            var allPoints = new List<Point3d> { originalLine.StartPoint };
            allPoints.AddRange(breakPoints);
            allPoints.Add(originalLine.EndPoint);

            var lines = new List<Line>();
            for (var i = 0; i < allPoints.Count - 1; i++)
            {
                var newLine = new Line(allPoints[i], allPoints[i + 1]);
                CopyLineProperties(originalLine, newLine);
                lines.Add(newLine);
            }

            return lines;
        }

        /// <summary>
        ///     复制线段属性
        /// </summary>
        private static void CopyLineProperties(Line source, Line target)
        {
            target.ColorIndex = source.ColorIndex;
            target.LayerId = source.LayerId;
        }

        /// <summary>
        ///     将线段添加到数据库
        /// </summary>
        private static void AddLinesToDatabase(BlockTableRecord blockTableRecord, Transaction transaction,
            List<Line> lines)
        {
            foreach (var line in lines)
            {
                blockTableRecord.AppendEntity(line);
                transaction.AddNewlyCreatedDBObject(line, true);
            }
        }

        #endregion
    }

    #region Point3d比较

    /// <summary>
    ///     Point3d扩展方法
    /// </summary>
    public static class Point3dExtensions
    {
        /// <summary>
        ///     检查两个点是否相同
        /// </summary>
        public static bool IsSameAs(this Point3d point1, Point3d point2)
        {
            if (point1 == point2)
                return true;

            return Math.Abs(FormatDoubleValue(point1.X) -
                            FormatDoubleValue(point2.X)) < double.Epsilon &&
                   Math.Abs(FormatDoubleValue(point1.Y) -
                            FormatDoubleValue(point2.Y)) < double.Epsilon;
        }

        /// <summary>
        ///     格式指定小数位数
        /// </summary>
        private static double FormatDoubleValue(double value)
        {
            var multiplier = Math.Pow(10, 3);
            return Math.Floor(value * multiplier) / multiplier;
        }
    }

    #endregion

    #region 上下文类

    /// <summary>
    ///     处理上下文类，封装处理过程中的共享数据
    /// </summary>
    internal class ProcessingContext
    {
        public ProcessingContext()
        {
            Document = Application.DocumentManager.MdiActiveDocument;
            Database = HostApplicationServices.WorkingDatabase;
            Editor = Document.Editor;
            Lines = new List<Line>();
            CirclePoints = new List<Point3d>();
            BlockPoints = new List<Point3d>();
            Polylines = new List<Polyline>();
        }

        public Database Database { get; }
        public Document Document { get; }
        public Editor Editor { get; }
        public SelectionSet SelectionSet { get; set; }
        public List<Line> Lines { get; }
        public List<Point3d> CirclePoints { get; }
        public List<Point3d> BlockPoints { get; }
        public List<Polyline> Polylines { get; }
    }

    #endregion
}